/*
 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     bstefanescu
 */
package org.nuxeo.gwt.habyt.upload.server;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.nuxeo.gwt.habyt.upload.client.Uploader;

/**
 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
 * 
 */
public class UploadServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    protected Provider provider;

    protected long maxFileSize = Long.MAX_VALUE;

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        FileProvider fp = null;
        String cname = config.getInitParameter(FileProvider.class.getName());
        if (cname != null) {
            try {
                fp = (FileProvider) Class.forName(cname).newInstance();

            } catch (Exception e) {
                throw new ServletException(e);
            }
        }
        provider = new Provider(fp);
        String maxsize = config.getInitParameter("max-file-size");
        if (maxsize != null) {
            maxFileSize = Long.parseLong(maxsize);
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String action = req.getParameter("action");
        if (action == null) {
            resp.sendError(500);
            return;
        }
        String id = req.getParameter("id");
        if (id == null) {
            resp.sendError(404);
            return;
        }
        try {
            if ("delete".equals(action)) {
                removeFile(req, id);
            } else if ("get".equals(action)) {
                writeFile(req, resp, id);
            } else if ("clear".equals(action)) {
                getUploadedFileManager(req).clear();
            } else if ("progress".equals(action)) {
                UploadedFile ref = (UploadedFile) getUploadedFileManager(req).get(
                        id);
                if (ref != null) {
                    resp.setHeader("X-FileUpload-Progress",
                            String.valueOf(ref.getProgress()));
                } else {
                    resp.setHeader("X-FileUpload-Progress", "0");
                }
            }
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        String id = req.getPathInfo();
        if (id == null || id.length() == 0 || id.equals("/")) {
            resp.sendError(404, "Should use POST /upload/file_ref");
            return;
        }

        if (id.startsWith("/")) {
            id = id.substring(1);
        }
        UploadedFile ref = new UploadedFile(id);
        UploadedFileManager mgr = getUploadedFileManager(req);
        // put the file ref in the session to be able to query progress.
        mgr.put(ref);

        try {

            FileItemFactory factory = getFileItemFactory(req.getContentLength());
            ServletFileUpload uploader = new ServletFileUpload(factory);
            uploader.setSizeMax(maxFileSize);
            uploader.setProgressListener(ref);

            List<FileItem> files = (List<FileItem>) uploader.parseRequest(req);

            if (!files.isEmpty()) {
                ref.setFile(files.get(0));
            }
            String r = "<div id=\"" + id + "\" title=\"" + ref.getContentType()
                    + "\">" + ref.getLength() + "</div>";
            resp.setContentLength(r.length());
            resp.setContentType("text/html");
            resp.getOutputStream().write(r.getBytes());
        } catch (Exception e) {
            throw new ServletException(e);
        } finally {
            // System.out.println("done.");
        }
    }

    protected FileItemFactory getFileItemFactory(int size) {
        return new DiskFileItemFactory();
    }

    public static synchronized UploadedFileManager getUploadedFileManager(
            HttpServletRequest req) {
        HttpSession session = req.getSession();
        String key = UploadedFileManager.class.getName();
        UploadedFileManager mgr = (UploadedFileManager) session.getAttribute(key);
        if (mgr == null) {
            mgr = new UploadedFileManager();
            session.setAttribute(key, mgr);
        }
        return mgr;
    }

    public void removeFile(HttpServletRequest req, String id) throws Exception {
        FileHolder fh = provider.get(req, id);
        if (fh != null) {
            fh.delete();
        }
        if (fh instanceof UploadedFile) {
            getUploadedFileManager(req).remove(id);
        }
    }

    public void writeFile(HttpServletRequest req, HttpServletResponse resp,
            String id) throws Exception {
        FileHolder fh = provider.get(req, id);
        if (fh != null) {
            resp.setContentType(fh.getContentType());
            long len = fh.getLength();
            if (len > 0) {
                resp.setContentLength((int) len);
            }
            String fn = fh.getFilename();
            if (fn != null) {
                resp.setHeader("Content-Disposition", "inline; filename=" + fn);
            }
            fh.writeTo(resp.getOutputStream());
        }
    }

    static class Provider implements FileProvider {
        FileProvider customProvider;

        public Provider(FileProvider customProvider) {
            this.customProvider = customProvider;
        }

        @Override
        public FileHolder get(HttpServletRequest req, String id) {
            if (id.startsWith(Uploader.UPLOADED_FILE_ID_PREFIX)) {
                return getUploadedFileManager(req).get(id);
            }
            return customProvider != null ? customProvider.get(req, id) : null;
        }
    }

}
